package com.enjoyor.soa.traffic.frame.support.solr.impl;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.enjoyor.soa.traffic.frame.support.solr.ISolrService;
import com.enjoyor.soa.traffic.frame.support.util.DataCheck;
import com.enjoyor.soa.traffic.util.exception.ManagerException;
import com.enjoyor.soa.traffic.util.pojo.Page;

@Service
public class SolrService implements ISolrService {

    private static final Logger logger = Logger.getLogger(SolrService.class);

    @Autowired
    protected SolrClient solrClient;

    @Override
    public boolean save(String coreName, Map<String, Object> map) {
        try {
            DataCheck.notEmpty(map, "save Method's param Map<String, Object> is null !");

            if (map.get("id") == null || StringUtils.isBlank(map.get("id").toString()))
                throw new ManagerException("save fail : the map can't contain id!");

            SolrInputDocument document = new SolrInputDocument();
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                document.addField(entry.getKey(), entry.getValue());
            }
            solrClient.add(coreName, document);
            solrClient.commit(coreName);
            return true;
        } catch (Exception e) {
            handlerException("insert", e);
        }
        return false;
    }

    @Override
    public boolean save(String coreName, Object object) {
        try {
            DataCheck.hasText(coreName, "save Method's param coreName is null !");
            DataCheck.notNull(object, "save Method's param object is null !");

            solrClient.addBean(coreName, object);
            solrClient.commit(coreName);
            return true;
        } catch (Exception e) {
            handlerException("insert", e);
        }
        return false;
    }

    @Override
    public boolean saveByReflect(String coreName, Object object) {
        try {

            DataCheck.hasText(coreName, "saveByReflect Method's param coreName is null !");
            DataCheck.notNull(object, "saveByReflect Method's param object is null !");

            Class<?> beanClass = object.getClass();
            SolrInputDocument document = new SolrInputDocument();
            for (Field field : beanClass.getDeclaredFields()) {
                String fieldName = field.getName();

                int modifier = field.getModifiers();
                if (Modifier.isStatic(modifier) || Modifier.isFinal(modifier))
                    continue;

                field.setAccessible(true);

                Object filedValue = field.get(object);

                if (field.isAnnotationPresent(org.apache.solr.client.solrj.beans.Field.class)) {

                    String aName = field.getAnnotation(org.apache.solr.client.solrj.beans.Field.class).value();

                    if (aName != null && !StringUtils.isBlank(aName)) {
                        fieldName = aName;
                    }

                }

                document.addField(fieldName, filedValue);

            }

            solrClient.add(coreName, document);
            solrClient.commit(coreName);

            return true;
        } catch (Exception e) {
            handlerException("insert", e);
        }
        return false;
    }

    @Override
    public int batchSave(String coreName, List<Object> list) {
        try {
            DataCheck.hasText(coreName, "batchSave Method's param coreName is null !");
            DataCheck.notEmpty(list, "batchInsert Method's param list is null !");
            solrClient.addBeans(coreName, list);
            solrClient.commit(coreName);
            return list.size();
        } catch (Exception e) {
            handlerException("batchInsert", e);
        }
        return 0;
    }

    @Override
    public boolean deleteById(String coreName, String id) {
        try {
            DataCheck.hasText(coreName, "deleteById Method's param coreName is null !");
            DataCheck.hasText(id, "deleteById Method's param id is null !");

            solrClient.deleteById(coreName, id);
            solrClient.commit(coreName);
            return true;
        } catch (Exception e) {
            handlerException("deleteById", e);
        }
        return false;
    }

    @Override
    public int deleteById(String coreName, List<String> ids) {
        try {
            DataCheck.hasText(coreName, "deleteById Method's param coreName is null !");
            DataCheck.notEmpty(ids, "deleteById Method's param ids is null !");

            solrClient.deleteById(coreName, ids);
            solrClient.commit(coreName);
            return ids.size();
        } catch (Exception e) {
            handlerException("deleteById", e);
        }
        return 0;
    }

    @Override
    public boolean deleteByQuery(String coreName, String query) {
        try {
            DataCheck.hasText(coreName, "deleteByQuery Method's param coreName is null !");
            DataCheck.hasText(query, "deleteByQuery Method's param query is null !");

            solrClient.deleteByQuery(coreName, query);
            solrClient.commit(coreName);
            return true;
        } catch (Exception e) {
            handlerException("deleteByQuery", e);
        }
        return false;
    }

    @Override
    public boolean deleteAll(String coreName) {
        try {
            DataCheck.hasText(coreName, "deleteAll Method's param coreName is null !");

            solrClient.deleteByQuery(coreName, "*:*");
            solrClient.commit(coreName);
            return true;
        } catch (Exception e) {
            handlerException("deleteAll", e);
        }
        return false;
    }

    @Override
    public SolrDocument selectById(String coreName, String id) {
        SolrDocument document = new SolrDocument();
        try {
            DataCheck.hasText(coreName, "selectById Method's param coreName is null !");
            DataCheck.hasText(id, "selectById Method's param id is null !");

            document = solrClient.getById(coreName, id);
        } catch (Exception e) {
            handlerException("selectById", e);
        }
        return document;
    }

    @Override
    public Object selectById(String coreName, String id, Class<?> clazz) {
        try {
            DataCheck.hasText(coreName, "selectBeanById Method's param coreName is null !");
            DataCheck.hasText(id, "selectBeanById Method's param id is null !");
            DataCheck.notNull(clazz, "selectById Method's param clazz is null !");

            SolrDocument document = solrClient.getById(coreName, id);

            return this.toBean(document, clazz);

        } catch (Exception e) {
            handlerException("selectById", e);
        }
        return null;
    }

    @Override
    public SolrDocumentList selectAll(String coreName) {
        try {
            DataCheck.hasText(coreName, "selectAll Method's param coreName is null !");

            SolrQuery queryStr = new SolrQuery("*:*");
            queryStr.set("start", 0);
            queryStr.set("rows", Integer.MAX_VALUE);
            QueryResponse rsp = solrClient.query(coreName, queryStr);
            return rsp.getResults();
        } catch (Exception e) {
            handlerException("selectAll", e);
        }
        return null;
    }

    @Override
    public List<Object> selectAll(String coreName, Class<?> clazz) {
        try {
            DataCheck.hasText(coreName, "selectAll Method's param coreName is null !");
            DataCheck.notNull(clazz, "selectAll Method's param clazz is null !");

            SolrQuery queryStr = new SolrQuery("*:*");
            queryStr.set("start", 0);
            queryStr.set("rows", Integer.MAX_VALUE);
            QueryResponse rsp = solrClient.query(coreName, queryStr);
            return this.toBeanList(rsp.getResults(), clazz);

        } catch (Exception e) {
            handlerException("selectAll", e);
        }
        return null;
    }

    @Override
    public SolrDocumentList selectList(String coreName, String... filterQuerys) {
        try {
            DataCheck.hasText(coreName, "selectList Method's param coreName is null !");

            SolrQuery queryStr = this.checkFilterQuerys(null, filterQuerys);
            queryStr.set("start", 0);
            queryStr.set("rows", Integer.MAX_VALUE);
            QueryResponse rsp = solrClient.query(coreName, queryStr);
            return rsp.getResults();
        } catch (Exception e) {
            handlerException("selectList", e);
        }
        return null;
    }

    @Override
    public List<Object> selectList(String coreName, Class<?> clazz, String... filterQuerys) {
        try {
            DataCheck.hasText(coreName, "selectList Method's param coreName is null !");
            DataCheck.notNull(clazz, "selectAll Method's param clazz is null !");

            SolrQuery queryStr = this.checkFilterQuerys(null, filterQuerys);
            queryStr.set("start", 0);
            queryStr.set("rows", Integer.MAX_VALUE);
            QueryResponse rsp = solrClient.query(coreName, queryStr);
            return this.toBeanList(rsp.getResults(), clazz);
        } catch (Exception e) {
            handlerException("selectList", e);
        }
        return null;
    }

    @Override
    public SolrDocumentList selectList(String coreName, String query, String... filterQuerys) {
        try {
            DataCheck.hasText(coreName, "selectList Method's param coreName is null !");

            SolrQuery queryStr = this.checkFilterQuerys(this.checkQuery(query), filterQuerys);
            queryStr.set("start", 0);
            queryStr.set("rows", Integer.MAX_VALUE);
            QueryResponse rsp = solrClient.query(coreName, queryStr);
            return rsp.getResults();
        } catch (Exception e) {
            handlerException("selectList", e);
        }
        return null;
    }

    @Override
    public List<Object> selectList(String coreName, String query, Class<?> clazz, String... filterQuerys) {
        try {
            DataCheck.hasText(coreName, "selectList Method's param coreName is null !");
            DataCheck.notNull(clazz, "selectAll Method's param clazz is null !");

            SolrQuery queryStr = this.checkFilterQuerys(this.checkQuery(query), filterQuerys);
            queryStr.set("start", 0);
            queryStr.set("rows", Integer.MAX_VALUE);
            QueryResponse rsp = solrClient.query(coreName, queryStr);
            return this.toBeanList(rsp.getResults(), clazz);
        } catch (Exception e) {
            handlerException("selectList", e);
        }
        return null;
    }

    @Override
    public int selectCount(String coreName, String query, String... filterQuerys) {
        try {

            DataCheck.hasText(coreName, "selectCount Method's param coreName is null !");

            SolrQuery queryStr = this.checkFilterQuerys(this.checkQuery(query), filterQuerys);

            queryStr.set("start", 0);
            queryStr.set("rows", Integer.MAX_VALUE);
            QueryResponse queryResponse = solrClient.query(coreName, queryStr);

            SolrDocumentList results = queryResponse.getResults();

            return new Long(results.getNumFound()).intValue();

        } catch (Exception e) {
            handlerException("selectList", e);
        }
        return 0;
    }

    @Override
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public Page selectPage(String coreName, String query, Page page, String... filterQuerys) {

        try {

            DataCheck.hasText(coreName, "coreName Method's param coreName is null !");
            DataCheck.notNull(page, "coreName Method's param page is null !");

            SolrQuery queryStr = this.checkFilterQuerys(this.checkQuery(query), filterQuerys);

            // 排序列
            String sortDir = page.getSortDir();
            // 排序方向
            String sortExp = page.getSortExp();
            if (this.existField(sortDir) && this.existField(sortExp)) {
                if (sortExp.equals(SolrQuery.ORDER.desc.toString()))
                    queryStr.addSort(sortDir, SolrQuery.ORDER.desc);
                else
                    queryStr.addSort(sortDir, SolrQuery.ORDER.asc);
            }

            // 分页
            int pageSize = page.getPageSize() <= 0 ? 10 : page.getPageSize();
            int currentPage = page.getCurrentPage() <= 0 ? 1 : page.getCurrentPage();

            page.setPageSize(pageSize);
            page.setCurrentPage(currentPage);

            queryStr.setStart(currentPage - 1);
            queryStr.setRows(pageSize);

            QueryResponse queryResponse = solrClient.query(coreName, queryStr);

            SolrDocumentList results = queryResponse.getResults();

            int total = new Long(results.getNumFound()).intValue();

            page.setCustomResult(results);

            int totalPage;

            if (total % pageSize == 0)
                totalPage = total / pageSize;
            else
                totalPage = total / pageSize + 1;

            page.setTotalPage(totalPage);

            page.setTotalCount(total);

        } catch (Exception e) {
            handlerException("selectPage", e);
        }
        return page;
    }

    private boolean existField(Object param) {
        if (param == null)
            return false;
        if (param instanceof String) {
            String str = (String) param;
            if (StringUtils.isBlank(str))
                return false;
        }
        return true;
    }

    private SolrQuery checkQuery(String query) {
        SolrQuery queryStr;
        if (query != null && !StringUtils.isBlank(query))
            queryStr = new SolrQuery(query);
        queryStr = new SolrQuery("*:*");
        return queryStr;
    }

    private SolrQuery checkFilterQuerys(SolrQuery queryStr, String... filterQuerys) {
        if (queryStr == null)
            queryStr = new SolrQuery("*:*");
        if (filterQuerys != null && !ArrayUtils.isEmpty(filterQuerys))
            if (filterQuerys[0] != null && !StringUtils.isBlank(filterQuerys[0]))
                queryStr.addFilterQuery(filterQuerys);
        return queryStr;
    }

    public void handlerException(String method, Exception e) {
        String exceptionMsg = String.format(String
                .format("SolrService execute exception, method: {%s}, Exception: {%s}", new Object[] { method, e }));
        logger.error(exceptionMsg);
        throw new ManagerException(exceptionMsg);
    }

    public Object toBean(SolrDocument solrDocument, Class<?> clazz) {
        try {

            if (solrDocument == null)
                return null;

            Object obj = clazz.newInstance();

            Field[] fields = clazz.getDeclaredFields();

            for (Field field : fields) {
                Object value = solrDocument.get(field.getName());
                BeanUtils.setProperty(obj, field.getName(), value);
            }

            return obj;
        } catch (Exception e) {
            throw new ManagerException(e);
        }
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    public List<Object> toBeanList(SolrDocumentList solrDocumentList, Class<?> clazz) {
        try {

            if (CollectionUtils.isEmpty(solrDocumentList))
                return null;

            List list = new ArrayList();
            for (SolrDocument record : solrDocumentList) {
                list.add(toBean(record, clazz));
            }

            return list;

        } catch (Exception e) {
            throw new ManagerException(e);
        }
    }

}
